Skip to content

feat(ai.agents): native Azure Bot + Teams channel for activity-protocol agents#8939

Open
v1212 wants to merge 18 commits into
Azure:mainfrom
v1212:feat/activity-protocol-simple
Open

feat(ai.agents): native Azure Bot + Teams channel for activity-protocol agents#8939
v1212 wants to merge 18 commits into
Azure:mainfrom
v1212:feat/activity-protocol-simple

Conversation

@v1212

@v1212 v1212 commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Closes #8955

Summary

Adds native azd support for provisioning the Azure Bot and Microsoft Teams channel that front an activity-protocol (Teams) hosted agent. This replaces the previous setup-instance-bot.ps1 / post-deploy.ps1 postdeploy scripts with first-class behavior wired into the azure.ai.agents extension's postdeploy / postdown lifecycle handlers.

The end-to-end user journey is now fully native:

azd ai agent init      # azure.yaml keeps the activity declarations
azd provision          # Foundry project + ACR
azd deploy             # agent version active  ->  Azure Bot created + Teams channel enabled
azd down               # agent + Azure Bot torn down

Non-activity agents are completely unaffected — every Teams/bot-specific code path sits behind a single gate on the agent's activity profile, so existing responses / invocations agents behave exactly as before.

What changed (file level)

File Change
internal/cmd/listen.go Wire the two new hooks: ensureActivityBot in postdeployHandler, teardownActivityBots in postdownHandler.
internal/cmd/listen_activity.go (new) ensureActivityBot (postdeploy bot provisioning), teardownActivityBots (best-effort postdown cleanup), printTeamsNextSteps, readEnvValue.
internal/project/activity_profile.go (new, + test) IsActivityProtocol / ResolveActivityProfile — the single gate that detects activity opt-in (via protocols: activity_protocol or agent_endpoint.protocols: activity).
internal/pkg/botservice/botservice.go (new, + test) EnsureBot (create/update Azure Bot), ensureTeamsChannel, DeleteBot, BotName, MessagingEndpoint. Narrow interfaces over armbotservice so tests use fakes (7 tests).
go.mod / go.sum Add resourcemanager/botservice/armbotservice v1.2.0.

Design

  • Scope boundary — azd owns the Azure resource plane only. It creates the Azure Bot, binds it to the agent instance identity, enables the Teams channel, and points the messaging endpoint at the agent. Teams app packaging and install stay on the M365/Graph plane and are out of scope; postdeploy prints a short next-steps guide for those manual steps.
  • Bot identity (Phase 1 "simple" use case). The bot msaAppId is the agent instance identity client id, which only exists after the agent version is created — so bot provisioning runs in postdeploy, after the version is active. msaAppType = SingleTenant, sku = F0, location = global, kind = azurebot.
  • Naming + teardown. The bot is named <agent-name>-bot-uai. BotService names are globally unique, so postdown deletes the bot to free the name for future redeploys (best-effort — failures are logged, never block azd down).
  • Messaging endpoint is pinned to …/agents/<agent-name>/endpoint/protocols/activityProtocol?api-version=2025-05-15-preview.
  • Idempotent. The bot is created via create-or-update, so re-running azd deploy neither duplicates nor errors.

Testing

Unitgo build ./..., go vet ./... clean; internal/project (activity round-trip) and internal/pkg/botservice (7 fake-backed tests) pass.

Live E2E — full journey validated in a real subscription (new resource group, greenfield Foundry, North Central US):

Stage Result
azd ai agent init Generated azure.yaml preserves the activity declarations (protocols: activity_protocol, agentEndpoint.protocols: [activity], authorizationSchemes: [BotServiceRbac]).
azd provision Foundry project + ACR created via the microsoft.foundry provider.
azd deploy Agent version active; Azure Bot <agent>-bot-uai created — msaAppType=SingleTenant, msaAppId = instance identity client id, sku=F0, location=global, kind=azurebot, Teams channel enabled, messaging endpoint = pinned activityProtocol URL, provisioningState=Succeeded. Verified directly against ARM.
re-azd deploy Idempotent — no duplicate bot, no error.
azd down Resource group deleted; bot removed and its global name freed.

Out of scope / follow-ups

Notes for reviewers

  • After an activity deploy, postdeploy writes a generic TEAMS_APP_SETUP.md next to the agent source. It is built only from runtime values (agent name, bot name, msaAppId) and links to the official Microsoft Learn docs for packaging and sideloading a Teams app, so it works for any activity agent (init-from-manifest or init-from-code) and is never written for non-activity deployments. A file is used rather than stdout because azd's progress UI does not surface postdeploy handler output.

Update: activity-protocol support in init-from-code

The init-from-local-code path now supports activity-protocol agents (previously only the init-from-manifest path did). Bringing your own activity code and running azd init (choosing activity_protocol, or --protocol activity_protocol) now produces an azure.yaml identical to the manifest path and fully satisfies azd deploy.

  • activity_protocol/v1 added to knownProtocols (opt-in; no-prompt default stays responses/2.0.0)
  • composes the agentEndpoint when activity is selected — advertises every selected protocol and appends authorizationSchemes: [BotServiceRbac] (pure-activity → protocols: [activity]), so the postdeploy bot gate fires for code-initialized agents too
  • activity coexists with other protocols — the endpoint advertises all selected protocols as siblings (matching the service's per-protocol config); there is no exclusivity restriction
  • the azd init flow is otherwise unchanged for non-activity agents

Scope: simple use case (Phase 1). Digital worker is Phase 2.


Testing & validation

Automated

  • Full azure.ai.agents extension suite: go build ./..., go vet ./..., go test ./... — green. (The only failures locally are internal/synthesis TestARMTemplate_MatchesBicepBuild / TestBrownfieldARMTemplate_MatchesBicepBuild, which are stale-ARM-vs-local-bicep environmental failures — templates/ and internal/synthesis/ are byte-identical to main, so unrelated to this PR.)
  • New unit tests: botservice (EnsureBot create/idempotent, Teams channel, DeleteBot), activity_profile (IsActivityProtocol / ResolveActivityProfile / ActivityAgentEndpoint), init_from_code (activity_protocol flag path + coexistence with other protocols + KnownProtocolNames).

Local azd init (built via azd x build, run through branch azd)

Scenario Result
init from manifest (activity) azure.yaml carries agentEndpoint{protocols:[activity], authorizationSchemes:[BotServiceRbac]} + protocols:[activity_protocol/v1]
init from code (--protocol activity_protocol) activity declaration byte-identical to the manifest path
init from code (default, no --protocol) protocols:[responses/2.0.0], no agentEndpoint — non-activity flow unchanged (regression check)
init from code (activity_protocol + responses) both kept — endpoint advertises protocols:[activity, responses] + authorizationSchemes:[BotServiceRbac]; activity coexists with responses

Live end-to-end (init-from-code activity project; sub ActivityProtocol-Dev, North Central US)

azd provision (2m24s, Foundry project + ACR) → azd deploy (3m7s, agent active) → postdeploy auto-provisioned the Azure Bot echocode-bot-uai (SingleTenant, msaAppId = agent instance identity, F0/global, endpoint pinned to the activityProtocol endpoint, MsTeams + WebChat + DirectLine channels enabled, provisioningState: Succeeded) → azd down --purge (1m45s, resource group deleted and bot name freed). Bot state verified directly via ARM (Microsoft.BotService/botServices, api-version 2022-09-15). The equivalent manifest-path project was validated the same way in earlier testing.

Two live reference environments (Teams-verified)

To exercise both azd ai agent init entry points, the same quickstart-echo activity agent was deployed twice — identical in every respect except the init entry point — and both were sideloaded into Microsoft Teams and confirmed to echo replies. Both deploys use ACR remote build (docker.remoteBuild: true), not ZIP code-deploy. Subscription: ActivityProtocol-Dev, region northcentralus. The flow was fully non-interactive with no manual patches (azd ai agent init -> azd provision -> azd deploy).

Env A — init from manifest Env B — init from code
Init command azd ai agent init -m agent.manifest.yaml --deploy-mode container azd ai agent init --deploy-mode container (from source, no manifest)
Foundry agent echomani07031202 echocode07031202
Resource group rg-mani07031202-69e12725 rg-code07031202-f68e547e
Foundry project mani07031202 code07031202
Azure Bot echomani07031202-bot-<salt> echocode07031202-bot-<salt>
Deploy mode container / ACR remote build container / ACR remote build
Teams reply verified verified

Each deploy auto-created the Foundry project + hosted agent, the Azure Bot with the Teams channel enabled, and the Azure AI User role for the instance identity. Both agents were sideloaded via a per-agent Teams app package and replied successfully in Teams, confirming the native manifest and code init paths are functionally equivalent end-to-end.


Follow-up: ship quickstart-echo as an azd ai agent init template

Add the quickstart-echo (simple activity/Teams) sample to the azd ai agent init template gallery so users can scaffold it directly. Not possible yet — the sample (foundry-samples: samples/python/hosted-agents/bring-your-own/activity/echo) is not merged yet. Once it lands, ship it as a native template with the changes below. Tracked in #8960.

To ship the sample as an azd template (native flow)

  • Remove azure.yaml, infra/, and the old scripts (post-deploy.ps1 / setup-instance-bot.ps1) — azd ai agent init scaffolds the native azure.yaml (foundry provider) and the extension handles the Bot + Teams channel in postdeploy.
  • Pin azure-ai-agentserver-core in requirements.txt to the same datestamp as ...-activity (avoids pulling an incompatible core -> container crash -> no Teams reply).
  • (Optional) Parameterize manifest.json name.short so multiple installs are distinguishable.

To just use the sample directly

  • Interactive azd ai agent init -> provision -> deploy (sub/location are picked in the init prompts — no manual env setup).
  • If core isn't pinned, pin it yourself to avoid the version risk.
  • Teams packaging + install is manual either way — azd deploy just emits a guide (TEAMS_APP_SETUP.md).

Bottom line: templating ~= drop azure.yaml/infra/old scripts + pin core; direct use is basically ready to run, with core-pinning the only real gotcha.

…ol agents

Provision the Azure Bot and Microsoft Teams channel for activity-protocol
(Teams) hosted agents natively during `azd deploy`, replacing the
setup-instance-bot.ps1 postdeploy script. The bot is bound to the agent
instance identity (msaAppId = instance identity client id), enabled on the
Teams channel, and its messaging endpoint is pointed at the agent's activity
protocol endpoint. Teardown deletes the bot so its globally unique name is
freed for redeploys. Non-activity agents are completely unaffected (single
gate on the activity profile).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added the ext-agents azure.ai.agents extension label Jul 2, 2026
Jian Wu and others added 2 commits July 2, 2026 16:31
Extend the init-from-local-code path so activity-protocol agents can be
initialized without a manifest, producing an azure.yaml identical to the
init-from-manifest path and fully satisfying azd deploy.

- add activity_protocol/v1 to knownProtocols (opt-in via --protocol or
  interactive multiselect; no-prompt default unchanged: responses/2.0.0)
- inject activity agentEndpoint (protocols: [activity], authorizationSchemes:
  [BotServiceRbac]) when IsActivityProtocol matches, so postdeploy bot gate
  fires for code-initialized activity agents just like manifest agents
- add validateProtocolSelection to reject combining activity_protocol with
  other protocols (activity agent exposes only the activity endpoint);
  enforced in both flag and interactive paths
- add ActivityAgentEndpoint helper + unit tests

The azd init flow is otherwise unchanged for non-activity agents.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- go fix: use new(x) for pointer-to-value and range-over-int (go 1.26)
  in botservice.go / botservice_test.go
- cspell: add activity/bot terms (botservice, armbotservice, Azurebot,
  idempotently, sideload, behaviour) to the extension dictionary

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@v1212 v1212 removed their assignment Jul 2, 2026
Jian Wu and others added 7 commits July 2, 2026 17:47
…eploy

Replace the sample-specific, console-only Teams next-steps hint with a generic TEAMS_APP_SETUP.md written next to the agent source during postdeploy. The guide is built only from runtime values (agent name, bot name, msaAppId) and links to official Microsoft Learn docs for packaging and sideloading a Teams app, so it works for any activity agent regardless of source (init-from-manifest or init-from-code) and is a no-op for non-activity deployments. Writing a file (not stdout) guarantees the user reliably receives the guidance, since azd's progress UI does not surface postdeploy handler stdout.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Address peer-review feedback on the activity-protocol bot flow:

- Salt the Azure Bot resource name with a hash of the deployment scope (subscription + resource group). BotService names are globally unique, so two environments deploying an agent with the same name previously collided; the name stays deterministic per scope so redeploys still update the same bot. Long agent names are truncated to the BotService handle limit. Create and teardown share BotScopeSalt so the names match.

- On postdeploy bot-config failure, make the error state that the agent version deployed successfully and only the Teams channel binding failed (commonly permissions/quota), and to re-run 'azd deploy'.

- Rename TestEnsureBotIsIdempotentAcrossRuns to TestEnsureBotIssuesUpsertOnEveryRun and clarify it asserts an upsert each run, not server-side idempotency; expand TestBotName to cover determinism, per-scope uniqueness, and length capping.

- Clarify the messaging-endpoint api-version constant is intentionally independent of the deploy api-version.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…d guide

Rewrite the generated TEAMS_APP_SETUP.md to include direct, minimal steps instead of only linking out: an easiest path (Teams Developer Portal, reuse the existing Azure bot by Bot ID, download a ready .zip) and a by-hand path (three-file list, a copy-paste minimal manifest.json noting that app id is a new GUID distinct from botId), plus numbered sideload steps (Apps -> Manage your apps -> Upload an app -> Upload a custom app). Each step keeps a link to the official Microsoft Learn doc for detail.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… fallback

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@v1212 v1212 marked this pull request as ready for review July 3, 2026 06:53
@v1212 v1212 requested a review from JeffreyCA as a code owner July 3, 2026 06:53
Copilot AI review requested due to automatic review settings July 3, 2026 06:53
…l-simple

# Conflicts:
#	cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds native azd support for provisioning the Azure Bot and Microsoft Teams channel that front an activity-protocol (Teams) hosted agent, replacing the previous setup-instance-bot.ps1 / post-deploy.ps1 postdeploy scripts. Bot provisioning is wired into the azure.ai.agents extension's postdeploy/postdown lifecycle handlers, and all Teams/bot-specific behavior sits behind a single activity-profile gate so non-activity agents are unaffected. It also extends the init-from-code path to support activity_protocol so it produces an azure.yaml identical to the manifest path.

Changes:

  • New botservice package (create/update Azure Bot, enable Teams channel, delete on teardown) with narrow SDK interfaces for fake-backed unit tests.
  • New activity-profile gate (IsActivityProtocol/ResolveActivityProfile) and postdeploy/postdown hooks (ensureActivityBot/teardownActivityBots) plus a generated TEAMS_APP_SETUP.md guide.
  • init-from-code now recognizes activity_protocol/v1, injects the activity agentEndpoint, and rejects combining it with other protocols.

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
internal/project/activity_profile.go (+test) Single gate detecting activity opt-in and the injected activity endpoint declaration.
internal/pkg/botservice/botservice.go (+test) Azure Bot create-or-update, Teams channel enable, delete, deterministic naming/messaging endpoint.
internal/cmd/listen.go Wires ensureActivityBot into postdeploy and teardownActivityBots into postdown.
internal/cmd/listen_activity.go (+test) Bot provisioning/teardown handlers, next-steps output, and Teams setup guide generation.
internal/cmd/init_from_code.go (+test) Adds activity_protocol, injects activity endpoint, enforces protocol exclusivity.
go.mod / go.sum Adds armbotservice v1.2.0 (verified to exist).
cspell.yaml Adds bot/Teams-related dictionary terms.

Findings are limited to a misleading bots[].id reference (should be bots[].botId) in the user-facing next-steps message and matching test comment, a errors.Aserrors.AsType convention nit, and a British-spelling ("behaviour") consistency nit. The core provisioning logic, gating, idempotency, and teardown look correct and are backed by unit tests and the described live E2E validation.

Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/listen_activity.go Outdated
Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/listen_activity_test.go Outdated
Comment thread cli/azd/extensions/azure.ai.agents/internal/project/activity_profile.go Outdated
…avior spelling

- Fix runtime/test message to reference Teams manifest bots[].botId (not bots[].id)
- Use Go 1.26 errors.AsType helper in botservice.isNotFound for consistency
- Use American spelling 'behavior' and drop the cspell behaviour allow-entry

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@jongio jongio left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Activity-protocol bot provisioning is well-gated behind IsActivityProtocol, idempotent via PUT, and correctly scoped to Phase 1 (simple use case). Teardown is properly best-effort. Test coverage looks solid across botservice, activity_profile, and init_from_code paths.

Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/init_from_code.go Outdated
var knownProtocols = []protocolInfo{
{Name: "responses", Version: "2.0.0"},
{Name: "invocations", Version: "1.0.0"},
{Name: "activity_protocol", Version: "v1"},

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't suppose this is something we can push on elsewhere for the version to be consistent with the other protocols?

@v1212 v1212 Jul 7, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — bumped to 1.0.0 for consistency with the other protocols (routing-equivalent to v1 on the platform). For context: neither value originated in this PR — v1 came from Vienna and the activity_protocol name from a pre-existing definition. Both are now defined in one central place and changed there.

@trangevi trangevi Jul 7, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be consistent with the service (for both the name and the version), otherwise there's potential for confusion as things change or are updated in the future. If there were going to be a change, it should be in the service, but without that this doesn't feel like something we should be customizing.

@therealjohn , any preference?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the service uses v1 we should use that - and if it does, where do we file a bug to track updating it to 1.0.0 like the other protocols?

@v1212 v1212 Jul 8, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Name: the service already renamed the union value  activity_protocol  →  activity .  AgentEndpointProtocol.ActivityValue = "activity"  is the current canonical;  activity_protocol  is kept only as  LegacyActivityProtocolValue  for read back-compat, and  ProtocolVersionRecord.IsActivityProtocol()  matches both ( AgentEndpointProtocol.cs ,  HostedAgentDefinitionExtensions.cs ). So  activity  matches the service's canonical name, our  IsActivityProtocolName  mirrors its dual-spelling gate, and there's no bug to file — that rename already landed service-side.

Version: the service accepts both  v1  and  1.0.0 , both routing to  /api/messages ; only  2.0.0  routes to  /activity/messages  ( MainContainerEnvironmentConstants.SupportedActivityProtocolVersions  +  GetActivityProtocolContainerPath ). Our sample serves  /api/messages , so  2.0.0  (the service "recommended") isn't usable; we pick  1.0.0  to match the semver form of  responses / invocations .  v1  is equally valid if you'd prefer the older spelling.

this PR adopts the service's renamed name ( activity ) and an accepted version ( 1.0.0 ) while keeping legacy  activity_protocol / v1  read support — so it's consistent with the service, not customizing. @trangevi @therealjohn

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why "our sample" is what we're basing this decision off of? If the service has moved to 2.0.0, I would expect we would want to as well?

Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/init_from_code.go Outdated
Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/init_from_code.go Outdated
Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/listen_activity.go
Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/listen_activity.go
- Drop Teams-specific wording from protocol/endpoint-layer comments; the
  Activity protocol is not unique to Teams
- Use activity protocol version 1.0.0 for consistency with other protocols
  (routing-equivalent to v1)
- Emit canonical protocol name "activity" instead of redundant
  "activity_protocol"; keep the legacy value accepted for back-compat
- Rewrite validateProtocolSelection doc/error to state the real reason
  (ActivityAgentEndpoint replaces AgentEndpoint wholesale, cannot compose)
- Clarify ensureActivityBot / EnsureBot / ensureTeamsChannel docs: azd
  provisions the Azure Bot + resource-plane Teams channel (a required
  connector bound to the agent identity), not the M365 Teams app
- Move the embedded Teams setup guide into assets/teams_app_setup_guide.md
  rendered via go:embed + text/template; rendered output is byte-identical

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@v1212 v1212 force-pushed the feat/activity-protocol-simple branch from fc785fc to ea1abee Compare July 7, 2026 04:08

@jongio jongio left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incremental review of the review-feedback commit (ea1abee). Changes cleanly address trangevi's feedback:

  • Protocol renamed to canonical activity with backward-compat IsActivityProtocolName() accepting the legacy activity_protocol value
  • Version aligned to 1.0.0 semver style with routing semantics documented
  • Template extracted to embedded assets/teams_app_setup_guide.md (lintable, editor-friendly)
  • Comments clarified: Azure Bot "Teams channel" (resource toggle) vs Teams "app" (M365 packaging/sideload) distinction is now explicit throughout
  • validateProtocolSelection comment rewritten to explain the Phase-1 scoping choice vs platform limitation
  • Test coverage properly updated for both old and new protocol names

No new issues. Backward compat is solid. CI lint/cspell/golangci-lint all green.

v1212 pushed a commit to v1212/azure-dev that referenced this pull request Jul 7, 2026

@RickWinter RickWinter left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This adds native provisioning of the Azure Bot and Microsoft Teams channel for activity-protocol agents, wired into the extension's postdeploy/postdown handlers, and extends the init-from-code path to emit the same activity declaration the manifest path already produced. The shape is right. A single ResolveActivityProfile gate keeps every Teams/bot code path off non-activity deploys, the botservice client is fronted by narrow botsAPI/channelsAPI interfaces so the seven tests run against fakes, and EnsureBot is a create-or-update so redeploys stay idempotent. The protocol constant rename from "activity_protocol" to "activity" is low-risk here: the canonical constant has no non-test caller, and detection flows through IsActivityProtocolName, which still accepts the legacy value.

One thing worth resolving before merge: the postdeploy bot provisioning is placed after the optimization-reporting early returns (missing FOUNDRY_PROJECT_ENDPOINT, missing AZURE_TENANT_ID, or a credential failure each return nil with a "skipping optimization reporting" log). For an activity agent those guards silently skip the required Teams bot while deploy reports success, which also contradicts the hard-fail path immediately below where an EnsureBot error fails the deploy. The happy path is fine because provision sets both values, so this is a failure-mode and coupling concern, not a common-case bug.

Leaving Teams app packaging and sideload out of azd is the correct scope boundary, and TEAMS_APP_SETUP.md being built only from runtime values keeps it generic. Build, vet, and the botservice/project/cmd tests pass locally.

Disposition: address the postdeploy coupling before merge; the rest can proceed.

Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go Outdated
Copilot AI added 3 commits July 8, 2026 12:01
…reporting guards

The postdeploy Teams bot (a required connector for activity agents) ran only
after early-return guards written for best-effort optimization reporting, so a
missing FOUNDRY_PROJECT_ENDPOINT / AZURE_TENANT_ID / credential silently skipped
the bot while 'azd deploy' reported success — inconsistent with the EnsureBot
error path that hard-fails.

Resolve the activity profile first, gather the shared inputs once (no skip-vs-fail
policy in the read), then let each step apply its own policy: the required bot
hard-fails on a missing input; best-effort optimization reporting logs and skips.
Non-activity agents are unaffected.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Activity is not exclusive: the platform models every protocol as a sibling
per-protocol entry on the same endpoint, so agent.yaml just needs to advertise
each selected protocol plus the BotServiceRbac scheme Activity requires.

- init now composes the activity endpoint (advertises all selected protocols +
  appends BotServiceRbac) instead of overwriting agent_endpoint wholesale
- remove validateProtocolSelection's activity-exclusive rejection
- non-activity path unchanged (agent_endpoint stays nil)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drives the real init-from-code assembly (compose + schema validation) and
init-from-manifest parse to prove Activity coexists with responses/invocations,
while the non-activity path stays unchanged (no agent_endpoint injected).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@RickWinter RickWinter left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This replaces the setup-instance-bot.ps1 / post-deploy.ps1 postdeploy scripts with native Azure Bot plus Teams-channel provisioning wired into the extension's postdeploy/postdown lifecycle, gated entirely behind an activity-protocol check so non-activity agents are untouched. The layering is right: the botservice package owns the ARM resource plane, activity_profile is the single gate, and the postdeploy input-gathering refactor cleanly separates the two policies (the required bot fails the deploy, best-effort optimization reporting only logs and skips). The narrow botsAPI/channelsAPI interfaces over armbotservice with fake-backed tests are the right call, EnsureBot idempotency and not-found-tolerant DeleteBot are handled correctly, and BotName salts on subscription plus resource group to avoid the global-uniqueness collision. The modern Go idioms (new(value), errors.AsType) are fine on 1.26.

The design point worth resolving is the activity exclusive-vs-coexistence contract. ComposeActivityAgentEndpoint is deliberately built to advertise multiple protocols on the endpoint and dedup them so activity can coexist with responses/invocations, but the description says the opposite (activity is exclusive, rejected via validateProtocolSelection). That function does not exist anywhere in the tree, and nothing prevents an agent from selecting activity plus responses, in which case postdeploy still binds a single bot to the activityProtocol endpoint while the agent is also advertised as responses. Decide which contract is intended: if activity is exclusive, the multi-protocol machinery in Compose is unreachable and can collapse to ActivityAgentEndpoint(); if coexistence is intended, the description needs correcting and the mixed case wants a test.

Separately, the description has drifted from the code in a few spots (knownProtocols is activity/1.0.0, not activity_protocol/v1; the bot is -bot-, not -bot-uai; the validateProtocolSelection exclusivity claim). Worth tidying so users and future reviewers are not misled.

Intentionally left alone and correct: the AgentProtocolActivityProtocol constant rename to "activity" is safe, since its only references are by symbol (models.go, models_test.go) and reads accept both values via IsActivityProtocolName. Disposition: address the contract question and the description drift before merge; the orphaned ActivityAgentEndpoint helper can follow. Nothing blocks; the core resource-plane logic is sound.

Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/init_from_code.go
Comment thread cli/azd/extensions/azure.ai.agents/internal/project/activity_profile.go Outdated
Copilot AI added 2 commits July 8, 2026 15:04
…l-simple

# Conflicts:
#	cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_api/models.go
…ixture

ActivityAgentEndpoint() was only referenced from activity_profile_test.go;
every production path goes through ComposeActivityAgentEndpoint. Move it into
the test as expectedActivityEndpoint() to keep the project package surface
minimal.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@trangevi trangevi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this last comment is in the "from code" flow, it's highly customizable and doesn't necessarily need to be a blocker in my mind.

{Name: "activity_protocol", Version: "v1"},
// "activity" is the canonical protocol name (legacy alias: "activity_protocol").
// Version is a platform routing selector, not a free version string: "v1" and
// "1.0.0" route to /api/messages, "2.0.0" to /activity/messages. Our sample

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, so why are we using 1.0.0 instead of 2.0.0? I'm assuming that the service wants things moved to 2.0.0, and idk what sample this comment is referring to but I'm guessing we shouldn't be basing API decisions on a client sample?

// deploy. It returns the written path, or "" on any failure (best-effort: never
// blocks or fails the deploy). The guide is deploy-agnostic and links to the
// official Microsoft Learn docs rather than any sample-specific scripts.
func writeTeamsSetupGuide(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should all of this logic go in with the "next steps" handling? That's what that stuff is there for, right?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ext-agents azure.ai.agents extension

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Activity] Support activity protocol

7 participants